"""
个性化推荐服务
实现基于用户画像和内容特征的个性化推荐功能
"""

import logging
from typing import List, Dict, Any, Optional
from sqlalchemy.orm import Session
from sqlalchemy import func, and_
import numpy as np
from datetime import datetime, timedelta
import uuid

from src.models.user import User
from src.models.user_preference import UserInterest, ContentItem, UserContentInteraction
from src.utils.logging import get_logger

logger = get_logger(__name__)

class RecommendationService:
    """个性化推荐服务类"""
    
    def __init__(self, db: Session):
        self.db = db
    
    def get_content_based_recommendations(self, user_id: str, limit: int = 10) -> List[Dict[str, Any]]:
        """
        基于内容的推荐
        
        Args:
            user_id: 用户ID
            limit: 推荐数量限制
            
        Returns:
            推荐内容列表
        """
        try:
            # 获取用户兴趣
            user_interests = self.db.query(UserInterest).filter(
                UserInterest.user_id == user_id
            ).all()
            
            if not user_interests:
                return self._get_popular_content(limit)
            
            # 获取所有内容项
            all_content = self.db.query(ContentItem).all()
            
            # 计算内容与用户兴趣的匹配度
            recommendations = []
            for content in all_content:
                score = self._calculate_content_interest_score(content, user_interests)
                if score > 0:
                    recommendations.append({
                        'content': content,
                        'score': score
                    })
            
            # 按分数排序并返回前N个
            recommendations.sort(key=lambda x: x['score'], reverse=True)
            return [{
                'id': rec['content'].id,
                'title': rec['content'].title,
                'description': rec['content'].description,
                'type': rec['content'].content_type,
                'tags': rec['content'].tags,
                'relevance_score': rec['score']
            } for rec in recommendations[:limit]]
            
        except Exception as e:
            logger.error(f"获取基于内容的推荐失败: {e}")
            return []
    
    def get_collaborative_recommendations(self, user_id: str, limit: int = 10) -> List[Dict[str, Any]]:
        """
        协同过滤推荐
        
        Args:
            user_id: 用户ID
            limit: 推荐数量限制
            
        Returns:
            推荐内容列表
        """
        try:
            # 简化版协同过滤：基于相似用户的行为推荐
            # 在实际应用中，可以使用更复杂的算法如矩阵分解
            
            # 获取目标用户交互过的内容
            user_interactions = self.db.query(UserContentInteraction).filter(
                UserContentInteraction.user_id == user_id
            ).all()
            
            if not user_interactions:
                return self._get_popular_content(limit)
            
            interacted_content_ids = [str(interaction.content_id) for interaction in user_interactions]
            
            # 查找与目标用户有相似行为的其他用户
            similar_users = self.db.query(UserContentInteraction.user_id).filter(
                and_(
                    UserContentInteraction.content_id.in_(interacted_content_ids),
                    UserContentInteraction.user_id != user_id
                )
            ).distinct().limit(20).all()
            
            similar_user_ids = [str(user.user_id) for user in similar_users]
            
            if not similar_user_ids:
                return self._get_popular_content(limit)
            
            # 获取相似用户喜欢但目标用户未交互过的内容
            recommended_content = self.db.query(ContentItem).join(UserContentInteraction).filter(
                and_(
                    UserContentInteraction.user_id.in_(similar_user_ids),
                    ~ContentItem.id.in_(interacted_content_ids),
                    UserContentInteraction.score >= 4.0  # 高评分内容
                )
            ).distinct().limit(limit).all()
            
            return [{
                'id': content.id,
                'title': content.title,
                'description': content.description,
                'type': content.content_type,
                'tags': content.tags,
                'relevance_score': 0.7  # 简化评分
            } for content in recommended_content]
            
        except Exception as e:
            logger.error(f"获取协同过滤推荐失败: {e}")
            return []
    
    def get_hybrid_recommendations(self, user_id: str, limit: int = 10) -> List[Dict[str, Any]]:
        """
        混合推荐（结合内容推荐和协同过滤）
        
        Args:
            user_id: 用户ID
            limit: 推荐数量限制
            
        Returns:
            推荐内容列表
        """
        try:
            # 获取基于内容的推荐
            content_based_recs = self.get_content_based_recommendations(user_id, limit * 2)
            
            # 获取协同过滤推荐
            collaborative_recs = self.get_collaborative_recommendations(user_id, limit * 2)
            
            # 合并推荐结果并去重
            all_recs = {}
            
            # 添加内容推荐结果
            for rec in content_based_recs:
                rec_id = str(rec['id'])
                if rec_id not in all_recs:
                    all_recs[rec_id] = rec
                    all_recs[rec_id]['score_components'] = {'content_based': rec['relevance_score']}
                else:
                    all_recs[rec_id]['score_components']['content_based'] = rec['relevance_score']
            
            # 添加协同过滤推荐结果
            for rec in collaborative_recs:
                rec_id = str(rec['id'])
                if rec_id not in all_recs:
                    all_recs[rec_id] = rec
                    all_recs[rec_id]['score_components'] = {'collaborative': rec['relevance_score']}
                else:
                    all_recs[rec_id]['score_components']['collaborative'] = rec['relevance_score']
            
            # 计算综合得分
            for rec_id, rec in all_recs.items():
                content_score = rec['score_components'].get('content_based', 0)
                collaborative_score = rec['score_components'].get('collaborative', 0)
                
                # 加权综合得分 (内容推荐权重0.6，协同过滤权重0.4)
                rec['relevance_score'] = 0.6 * content_score + 0.4 * collaborative_score
            
            # 按综合得分排序
            sorted_recs = sorted(all_recs.values(), key=lambda x: x['relevance_score'], reverse=True)
            
            return sorted_recs[:limit]
            
        except Exception as e:
            logger.error(f"获取混合推荐失败: {e}")
            return []
    
    def _calculate_content_interest_score(self, content: ContentItem, user_interests: List[UserInterest]) -> float:
        """
        计算内容与用户兴趣的匹配度分数
        
        Args:
            content: 内容项
            user_interests: 用户兴趣列表
            
        Returns:
            匹配度分数
        """
        content_tags = getattr(content, 'tags', None)
        if not content_tags:
            return 0.0
        
        score = 0.0
        content_tags_set = set(content_tags) if isinstance(content_tags, list) else set()
        
        for interest in user_interests:
            if interest.interest_tag in content_tags_set:
                # 兴趣匹配得分 = 兴趣强度 * 时间衰减因子
                last_interacted = getattr(interest, 'last_interacted', None)
                time_decay = self._calculate_time_decay(last_interacted)
                weight = getattr(interest, 'weight', 0) or 0
                score += float(weight) * time_decay
        
        return float(score)
    
    def _calculate_time_decay(self, last_interacted) -> float:
        """
        计算时间衰减因子
        
        Args:
            last_interacted: 最后交互时间
            
        Returns:
            时间衰减因子 (0-1)
        """
        if not last_interacted:
            return 1.0
        
        # 计算天数差
        days_diff = (datetime.utcnow() - last_interacted).days
        
        # 指数衰减函数: e^(-days/30)
        decay_factor = np.exp(-days_diff / 30.0)
        
        return max(0.1, float(decay_factor))  # 最小值为0.1
    
    def _get_popular_content(self, limit: int = 10) -> List[Dict[str, Any]]:
        """
        获取热门内容（用于冷启动）
        
        Args:
            limit: 数量限制
            
        Returns:
            热门内容列表
        """
        try:
            # 获取交互次数最多的内容
            popular_content = self.db.query(
                ContentItem,
                func.count(UserContentInteraction.id).label('interaction_count')
            ).join(UserContentInteraction).group_by(ContentItem.id).order_by(
                func.count(UserContentInteraction.id).desc()
            ).limit(limit).all()
            
            return [{
                'id': content.id,
                'title': content.title,
                'description': content.description,
                'type': content.content_type,
                'tags': content.tags,
                'relevance_score': 0.5  # 默认得分
            } for content, _ in popular_content]
            
        except Exception as e:
            logger.error(f"获取热门内容失败: {e}")
            return []
    
    def record_user_interaction(self, user_id: str, content_id: str, interaction_type: str, score: float = 0.0):
        """
        记录用户与内容的交互
        
        Args:
            user_id: 用户ID
            content_id: 内容ID
            interaction_type: 交互类型
            score: 交互评分
        """
        try:
            interaction = UserContentInteraction(
                user_id=uuid.UUID(user_id) if isinstance(user_id, str) else user_id,
                content_id=uuid.UUID(content_id) if isinstance(content_id, str) else content_id,
                interaction_type=interaction_type,
                score=score
            )
            
            self.db.add(interaction)
            self.db.commit()
            
            # 更新用户兴趣
            self._update_user_interests(user_id, content_id, score)
            
        except Exception as e:
            logger.error(f"记录用户交互失败: {e}")
            self.db.rollback()
    
    def _update_user_interests(self, user_id: str, content_id: str, score: float):
        """
        根据用户交互更新用户兴趣
        
        Args:
            user_id: 用户ID
            content_id: 内容ID
            score: 交互评分
        """
        try:
            # 获取内容标签
            content = self.db.query(ContentItem).filter(ContentItem.id == content_id).first()
            content_tags = getattr(content, 'tags', None) if content else None
            if not content or not content_tags:
                return
            
            for tag in content_tags:
                # 查找是否已有该兴趣标签
                interest = self.db.query(UserInterest).filter(
                    and_(
                        UserInterest.user_id == user_id,
                        UserInterest.interest_tag == tag
                    )
                ).first()
                
                if interest:
                    # 更新兴趣强度（基于评分）
                    weight = getattr(interest, 'weight', 0) or 0
                    new_weight = min(1.0, float(weight) + (score / 5.0) * 0.1)
                    setattr(interest, 'weight', new_weight)
                    setattr(interest, 'last_interacted', datetime.utcnow())
                    # 更新与内容类别相关的元数据
                    category = "default_category"
                    content_metadata = getattr(content, 'content_metadata', None)
                    if content_metadata:
                        if isinstance(content_metadata, dict):
                            category = content_metadata.get("category", "default_category")
                        else:
                            category = "default_category"
                    # 使用setattr来避免类型检查错误
                    setattr(interest, 'category', category)
                else:
                    # 创建新的兴趣标签
                    initial_weight = (score / 5.0) * 0.5  # 初始权重基于评分
                    category = "default_category"
                    content_metadata = getattr(content, 'content_metadata', None)
                    if content_metadata:
                        if isinstance(content_metadata, dict):
                            category = content_metadata.get("category", "default_category")
                        else:
                            category = "default_category"
                        
                    new_interest = UserInterest(
                        user_id=uuid.UUID(user_id) if isinstance(user_id, str) else user_id,
                        interest_tag=tag,
                        weight=initial_weight,
                        category=category
                    )
                    self.db.add(new_interest)
            
            self.db.commit()
            
        except Exception as e:
            logger.error(f"更新用户兴趣失败: {e}")
            self.db.rollback()

# 全局实例
def get_recommendation_service(db: Session) -> RecommendationService:
    """获取推荐服务实例"""
    return RecommendationService(db)